package com.ruolin.micro.gateway.server.route.resolver;

import cn.hutool.core.collection.CollectionUtil;
import com.ruolin.micro.gateway.server.bean.RequestParamResolverHolder;
import com.ruolin.micro.gateway.server.constant.FormType;
import com.ruolin.micro.gateway.server.metadata.MethodRouteMetadata;
import com.ruolin.micro.gateway.server.metadata.ParameterMetadata;
import com.ruolin.micro.gateway.server.route.RouteDefinitionLocator;
import com.ruolin.micro.gateway.server.servlet.bean.RouterBeanDefinition;
import com.ruolin.micro.gateway.server.utils.DataTypePatternUtils;
import com.ruolin.micro.gateway.server.utils.ParameterUtils;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

public class RequestFormParameterResolver extends AbstractRequestParameterResolver {
    @Autowired
    private RouteDefinitionLocator routeDefinitionLocator;

    @Override
    public Object[] resolve(HttpServletRequest request, HttpServletResponse response) {
        String requestRoute = request.getPathInfo();
        List<RouterBeanDefinition> routerBeanDefinitions = routeDefinitionLocator.getRouteDefinitions();
        MethodRouteMetadata methodRouteMetadata = getMethodRouteMetadata(requestRoute, routerBeanDefinitions);

        RouterBeanDefinition routerBeanDefinition = getRouterBeanDefinition(requestRoute, routerBeanDefinitions);
        String routePart = requestRoute.substring(routerBeanDefinition.getPath().length());

        if (methodRouteMetadata.match(routePart) && methodRouteMetadata.hasJavaType()) {
            RequestParameterResolver pathVariableParameterResolver = RequestParamResolverHolder
                    .getResolverHolder().getResolver(FormType.PATH);
            return pathVariableParameterResolver.resolve(request, response);
        }
        List<String> types = methodRouteMetadata.getParameterTypes();
        Object[] args = null;

        String queryString = request.getQueryString();
        if (CollectionUtil.isNotEmpty(types)) {
            List<ParameterMetadata> parameters = ParameterUtils.getParameters(queryString);

            Map<String, Object> parameterMap = ParameterUtils.buildParameterMap(parameters);
            int objectIndex = methodRouteMetadata.getObjectIndex();
            int typeLen = methodRouteMetadata.getTypeLen();
            args = new Object[typeLen];

            if (types.size() == 1 && objectIndex == -1) {
                if (parameterMap.size() != typeLen) {
                    throw new IllegalArgumentException("args.length != types.length");
                }
                return parameterMap.values().toArray();

            } else if (methodRouteMetadata.getObjectIndex() != -1) {
                Map<String, Object> fieldMap = methodRouteMetadata.getFieldMap();
                for (Map.Entry<String, Object> entry : fieldMap.entrySet()) {
                    if (parameterMap.containsKey(entry.getKey())) {
                        fieldMap.put(entry.getKey(), parameterMap.get(entry.getKey()));
                        parameterMap.remove(entry.getKey());
                    }
                }

                int argLen = parameterMap.size() + 1;

                if (argLen != typeLen) {
                    throw new IllegalArgumentException("args.length != types.length");
                }

                List<Object> parameterValues = Arrays.asList(parameterMap.values().toArray());

                for (int i = 0; i < parameterValues.size(); i++) {
                    Object value = parameterValues.get(i);

                    int typeIndex = i;
                    if ((i == 0 && objectIndex == i)) {
                        args[0] = fieldMap;
                        typeIndex++;

                    } else {
                        args[objectIndex] = fieldMap;
                    }

                    if (value.getClass().getName().equals(types.get(typeIndex)) || DataTypePatternUtils.dataTypeMatch(types.get(typeIndex), value)) {
                        args[typeIndex] = parameterValues.get(i);

                    } else {
                        throw new IllegalArgumentException("parameter data types do not match,type:::: " + parameterValues.get(i).getClass().getName());
                    }
                }
            } else if (methodRouteMetadata.hasArrayType()) {
                List<ParameterMetadata> parameterMetadata = ParameterUtils.getParameters(queryString);
                Object[] objects = ParameterUtils.buildParameter(parameterMetadata);
                return objects;
            }

            parameterMap.clear();
            return args;
        }

        return Collections.emptyList().toArray();
    }

    @Override
    public RequestFormParameterResolver getResolver() {
        return this;
    }

    @Override
    public FormType getFormType() {
        return FormType.GET;
    }
}
